// #if 0
#include "containor.h"
#include "baseactor.h"
#include "drawer.h"
#include "updater.h"
#include <cstddef>
#include <cstdio>
#include <memory>
// Interface Implements
std::shared_ptr<Containor> FailSafe = std::make_shared<Containor>();
using ContainorUDIF = ContainorUpdaterInterface;
ContainorUDIF& Containor::Position(Coord_t pos)
{
    this->buffer.pos = pos;
    return *this;
}
ContainorUDIF& Containor::Size(Coord_t size)
{
    this->buffer.size = size;
    return *this;
}
ContainorUDIF& Containor::BorderStyle(char c)
{
    this->buffer.drawchar = c;
    return *this;
}
ContainorUDIF& Containor::Offset(Coord_t offset)
{
    this->buffer.offset = offset;
    return *this;
}
Action_t Containor::Commit()
{
    // if (attributes == buffer)
    attributes = buffer;
    return 0;
}
Action_t Containor::Rollback()
{
    // if (attributes == buffer)
    buffer = attributes;
    return 0;
}

// Containor
Containor::Containor()
    : ContainorUDIF(), Component(nullptr, nullptr),
      attributes({25, 25}, {5, 5}, ' ') //,containor_count(0), badresult(this),
{
}
Containor::Containor(Coord_t pos, Coord_t size, char c, Drawer* ptr)
    : attributes(pos, size, c), Component(ptr, nullptr), buffer(attributes)
{
}
Containor::Containor(Coord_t pos, Coord_t size, char c, Drawer* ptr, Containor* fa)
    : attributes(pos, size, c), Component(ptr, fa), buffer(attributes) //, father(fa)
{
}
Containor::~Containor() {}

// Component*& Containor::at(int i)
// {
//     if (this->_contain[i] && i >= 0 && i < 4)
//         return this->_contain[i];
//     else
//         return badresult;
// }

// void Containor::set(Containor* Content, int i)
// {
//     if (!this->_contain[i]) containor_count++;
//     this->_contain[i] = Content;
// }
// void Containor::add(Containor* Content)
// {
//     if (this->_contain[containor_count])
//     {
//         this->_contain[containor_count] = Content;
//         containor_count++;
//     }
//     else
//     {
//         for (auto& single : this->_contain)
//         {
//             if (single) single = Content;
//         }
//     }
// }

// let register to automatic set the pos and size?
void Containor::bindDrawer(Drawer* ptr_drawer)
{
    this->ptr_drawer = ptr_drawer;
}

using Contained = CoordGroup;
using Socket = Component;
void Containor::UpdateChildCoord(Socket* topleft, Socket* topright, Socket* bottomleft, Socket* bottomright)
{
    Coord_t pos{LocalPos.append(attributes.pos)};
    Coord_t size{LocalSize.append(attributes.size)};
    if (!bindPos) pos = attributes.pos;
    if (!bindSize) size = attributes.size;

    Full FullLayout(pos, size);
    LeftRight LRLayout(pos, size);
    UpDown UDLayout(pos, size);
    FourGrid FGlayout(pos, size);

    if (!topleft) topleft = FailSafe.get();
    if (!topright) topright = FailSafe.get();
    if (!bottomleft) bottomleft = FailSafe.get();
    if (!bottomright) bottomright = FailSafe.get();

    // TODO: before SetLocalCoord() , loop for detect nullptr, and redirect them to a const component
    switch (containStatus)
    {
        // Single component : AUTO  (it Fills all the space)
    case 0:
        return;
    case (TOP_LEFT):
        topleft->LocalCoordSet(FullLayout.Content);
        break;
    case (TOP_RIGHT):
        topright->LocalCoordSet(FullLayout.Content);
        break;
    case (BOTTOM_LEFT):
        bottomleft->LocalCoordSet(FullLayout.Content);
        break;
    case (BOTTOM_RIGHT):
        bottomright->LocalCoordSet(FullLayout.Content);
        break;

        //
        // Two Components : AUTO (Parrllel -> '=' or '||')
        // LEFT and RIGHT --> '||'
    case (TOP_LEFT + TOP_RIGHT):
        topleft->LocalCoordSet(LRLayout.Left);
        topright->LocalCoordSet(LRLayout.Right);
        break;
    case (BOTTOM_LEFT + BOTTOM_RIGHT):
        bottomleft->LocalCoordSet(LRLayout.Left);
        bottomright->LocalCoordSet(LRLayout.Right);
        break;

        // UP and Down  --> '='
    case (TOP_LEFT + BOTTOM_LEFT):
        topleft->LocalCoordSet(UDLayout.Up);
        bottomleft->LocalCoordSet(UDLayout.Down);
        break;
    case (TOP_RIGHT + BOTTOM_RIGHT):
        topright->LocalCoordSet(UDLayout.Up);
        bottomright->LocalCoordSet(UDLayout.Down);
        break;

        //
        // bigger than 3 : Not AUTO
    default:
        topleft->LocalCoordSet(FGlayout.TopLeft);
        topright->LocalCoordSet(FGlayout.TopRight);
        bottomleft->LocalCoordSet(FGlayout.BottomLeft);
        bottomright->LocalCoordSet(FGlayout.BottomRight);
        break;
    }
}
Action_t Containor::Zerolize()
{
    return this->Position(ZERO).Size(ZERO).Commit();
}

inline int SocketToID(const SocketList& SocketCode)
{
    switch (SocketCode)
    {
    case TOP_LEFT:
        return 0;
    case TOP_RIGHT:
        return 1;
    case BOTTOM_LEFT:
        return 2;
    case BOTTOM_RIGHT:
        return 3;
    default:
        return -1;
    }
}
inline void _ResetFather(Component* child, Containor* newFather)
{
    // TOP_LEFT TOP_RIGHT BOTTOM_LEFT BOTTOM_RIGHT
    //    0         1          2           3
    if (child == nullptr || newFather == nullptr)
    {
        return;
    }

    // Purpose: to Overide the old father
    // This code is replaceing the following codes:
    //      Element->WhichSocket() returns the enumCode of Socket,
    //      which would be converted into actual internal ID
    //        of Element array, by SocketToID(SocketList).
    // Eventually, the Socket would be cleared, to prepare for override
    if (child->getFather()) child->getFather()->clearElement(SocketToID(child->WhichSocket()));
    //  error  &(*this) would be (Containor*)(0xbaadf00dbaadf00d), when calling this->clearElement;

    child->SetFather(newFather);
    /* //Original Code
    switch (child->WhichSocket())
    {
        case TOP_LEFT:
        OriFather->clearElement(0);
        child->SetFather(newFather);
        break;
        case TOP_RIGHT:
        OriFather->clearElement(1);
        child->SetFather(newFather);
        break;
        case BOTTOM_LEFT:
        OriFather->clearElement(2);
        child->SetFather(newFather);
        break;
        case BOTTOM_RIGHT:
        OriFather->clearElement(3);
        child->SetFather(newFather);
        break;
        default:
        break;
    }
    */
}

// TODO: Need to rewrite to be more readable;
void Containor::Load(Socket* topleft, Socket* topright, Socket* bottomleft, Socket* bottomright)
{
    // int i{0};
    containStatus = EMPTY;

    // Make the parameters into array, to short the code
    Socket* ChildField[4]{topleft, topright, bottomright, bottomright};
    // static, since we don't need to refrash them
    static const SocketList SocketField[4]{TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT};
    static const char* SocketName[4]{"TopLeft", "TopRight", "BottomLeft", "BottomRight"};

    for (int i{0}; i < 4; i++)
    {
        Socket* element{ChildField[i]};

        if (!element) continue;

        printf("[%d]: is reseting the father of %p to %p\n", i, element, this);
        // UpCasting :   Since only Containers could loading things.
        //               This is a Safe Behavior to do Upcasting in loading

        // error : CHILDREN ARE NOT RENDERING
        if (element->getFather())
        {
            // element->getFather()->clearElement(SocketToID(element->WhichSocket()));
            printf("[%d]: the father of %p is set to %p\n", i, element, element->getFather());
        }
        this->_contain[i] = element;
        element->SetFather(this);

        containStatus += SocketField[i];
        element->setPos(SocketField[i]);
        printf("%s is binded", SocketName[i]);

        printf("[%d]: Zerolizing and binding new drawer\n", i);
        element->Zerolize();
        element->bindDrawer(this->ptr_drawer);
    }

    /*
        for (auto& ele : this->_contain)
        {
            switch (i)
            {
            case 0:
                ele = topleft;
                if (ele != nullptr)
                {
                    containStatus += TOP_LEFT;
                    ele->setPos(TOP_LEFT);
                }
                printf("Top left is binded\n");
                break;
            case 1:
                ele = topright;
                if (ele != nullptr)
                {
                    containStatus += TOP_RIGHT;
                    ele->setPos(TOP_RIGHT);
                }
                printf("Top right is binded\n");
                break;
            case 2:
                ele = bottomleft;
                if (ele != nullptr)
                {
                    containStatus += BOTTOM_LEFT;
                    ele->setPos(BOTTOM_LEFT);
                }
                printf("bottom left is binded\n");
                break;
            case 3:
                ele = bottomright;
                if (ele != nullptr)
                {
                    containStatus += BOTTOM_RIGHT;
                    ele->setPos(BOTTOM_RIGHT);
                }
                printf("bottom right is binded\n");
                break;
            default:
                containStatus = EMPTY;
                ele->setPos(EMPTY);
                break;
            }
            // if(ele){

            if (ele && ele->getFather())
            {
                printf("[%d]: is reseting the father of %p to %p\n", i, ele, this);
                Containor* temp = (Containor*)ele;
                // UpCasting :   Since only Containers could loading things.
                //               This is a Safe Behavior to do Upcasting in loading
                temp->setElement(SocketToID(ele->WhichSocket()), this);
                printf("[%d]: the father of %p is set to %p", i, ele, ele->getFather());
            }
            if (ele) // have to judge once, since father was reset above
            {
                printf("[%d]: Zerolizing and binding new drawer\n", i);
                ele->Zerolize();
                ele->bindDrawer(this->ptr_drawer);
            }

            //}
            // else
            //     ele = FailSafe.get();
            i++;
        }*/
    // this->UpdateChildCoord(_contain[0], _contain[1], _contain[2], _contain[3]);
}
Action_t Containor::SetFather(Containor* fa)
{
    this->father = fa;
    return 1;
}
void Containor::render()
{
    this->UpdateChildCoord(_contain[0], _contain[1], _contain[2], _contain[3]);
    for (auto ele : _contain)
    {
        if (ele != nullptr)
        {
            ele->render();
        }
    }
    if (ptr_drawer)
    {
        // printf("rendering containor: 0x%p\n", this);
        ptr_drawer->Square(LocalPos.append(attributes.pos), LocalSize.append(attributes.size), '-', '|', '+', false);
        // ptr_drawer->text(LocalPos.append(attributes.pos), "X---|", {4, 3}, false);
        // ptr_drawer->text(LocalPos.append(attributes.pos).append(LocalSize.append(attributes.size).append(-1, -1)),
        // "O",
        //                  {1, 1}, false);
    }
    // ptr_drawer->Square(attributes);
}

// #endif